package team.seekers.table.spider;

import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import okhttp3.Response;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Component;
import team.seekers.table.entity.Sysuser;
import team.seekers.table.entity.temp.CourseTable;
import team.seekers.table.entity.vo.User;
import team.seekers.table.utils.HttpUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * date: 2021/4/24 13:18
 *
 * @author GH
 * QQ 1124813753
 */
@Component
public class CoursesSpider {

    private static final String HOST = "https://jw.v.zzu.edu.cn/eams/";

    /**
     * 根据user爬取课程表
     * @param user 用户对象
     * @param acquireAll 是否获得全部课表，true为获取所有
     * @return 课表集合，如果acquireAll为false则List集合只有一个元素
     */
    public List<CourseTable> getCourseTable(User user, boolean acquireAll) {
        // 第一次请求获取动态密码以及cookies
        Response firstRes = HttpUtil.get(HOST + "login.action", null);
        List<String> cookies = firstRes.headers("Set-Cookie");
        String jssesionId = cookies.get(0).substring(0, 51);
        String gsessionId = cookies.get(1).substring(0, 49);
        // 截取UUID
        String loginHtml = HttpUtil.getContent(firstRes);
        int start = loginHtml.indexOf("CryptoJS.SHA1") + 2 + 13;
        String uuid = loginHtml.substring(start, start + 37);

        try {
            // 防止过快点击
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 第二次请求提交表单
        Map<String, String> headers = new HashMap<>();
        headers.put("Cookie", jssesionId + gsessionId);

        Map<String, String> data = new HashMap<>();
        data.put("username", user.getUsername());
        data.put("password", SecureUtil.sha1(uuid + user.getPassword()));
        // 若后续必须需要验证码可改进
        // data.put("captcha_response", user.getVerifyCode());

        Response loginRes = HttpUtil.post(HOST + "login.action", headers, data);
        List<CourseTable> tables = null;
        if ("/eams/home.action".equals(loginRes.request().url().encodedPath())) {
            // 如果响应请求路径为home说明重定向没有被拦截到登录页，登录成功
            // 第三次请求获取当前学期Id
            Response semesterRes = HttpUtil.get(HOST + "courseTableForStd.action?_="
                    + System.currentTimeMillis(), headers);
            String semesterId = semesterRes.header("Set-Cookie").substring(12, 12 + 2);

            try {
                // 防止过快点击
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 第四次请求获取课表bar以此获取semesterIds
            String barHtml = HttpUtil.getHtml(HOST + "courseTableForStd.action?_="
                    + System.currentTimeMillis(), headers, null);
            Document document = Jsoup.parse(barHtml);
            String barElemId = document.selectFirst(".toolbar").id();
            // 以及获取ids
            String script = document.getElementsByTag("script").last().html();
            int index = script.indexOf("form,\"ids\",\"");
            String ids = script.substring(index + 12, index + 12 + 6);

            // 如果不获取所有课表则只需要得到当前学期课表
            if (!acquireAll) {
                CourseTable courseTable = new CourseTable();
                String[][] courses = getCourses(semesterId, headers, ids);
                courseTable.setSemesterId(Integer.parseInt(semesterId));
                courseTable.setCourses(courses);

                tables = new ArrayList<>();
                tables.add(courseTable);
            } else {
                // 第五次请求获取semesterIds
                data = new HashMap<>();
                data.put("tagId", "semesterBar" + barElemId.substring(7) + "Semester");
                data.put("dataType", "semesterCalendar");
                data.put("value", semesterId);
                data.put("empty", "false");
                Response semestersRes = HttpUtil.post(HOST + "dataQuery.action", headers, data);
                // 根据学号查出课程起始年份
                int startYear = Integer.parseInt(user.getUsername().substring(0, 4));
                tables = getSemesterIds(HttpUtil.getContent(semestersRes), startYear);

                tables.forEach(courseTable -> {
                    // 第六次请求获取课表
                    String[][] courses = getCourses(String.valueOf(courseTable.getSemesterId()), headers, ids);
                    courseTable.setCourses(courses);
                });
            }
            // 最后再关闭
            HttpUtil.closeClient();
        }

        // 如果被重定向拦截了为null
        return tables;
    }

    private List<CourseTable> getSemesterIds(String semesterJson, Integer startYear) {
        Map map = JSON.parseObject(semesterJson, Map.class);
        Map semesterMap = (Map) map.get("semesters");

        List<CourseTable> res = new ArrayList<>();
        for (int i = 0; i < semesterMap.size(); i++) {
            List<Map> tables = (List<Map>) semesterMap.get("y" + i);
            tables.forEach(tableMap -> {
                String courseYear = (String) tableMap.get("schoolYear");
                // 课程年份在学生入校后才需要查询课表
                if (Integer.parseInt(courseYear.substring(0, 4)) >= startYear) {
                    CourseTable courseTable = new CourseTable();
                    courseTable.setSemesterId((Integer) tableMap.get("id"));
                    courseTable.setSchoolYear(courseYear);
                    courseTable.setTerm((String) tableMap.get("name"));
                    res.add(courseTable);
                }
            });
        }
        return res;
    }

    /**
     * 发起获取课程请求
     *
     * @param semesterId 学期ID
     * @param headers    请求头
     * @return 课程数组
     */
    private String[][] getCourses(String semesterId, Map<String, String> headers, String ids) {
        Map<String, String> data = new HashMap<>();
        data.put("setting.kind", "std");
        data.put("semester.id", semesterId);
        data.put("startWeek", null);
        data.put("project.id", "1");
        data.put("ids", ids);

        String courseHtml = HttpUtil.getHtml(HOST + "courseTableForStd!courseTable.action", headers, data);
        return getCourseVals(courseHtml);
    }

    /**
     * 课程HTML转化为字符串值数组
     */
    private String[][] getCourseVals(String courseHtml) {
        Document document = Jsoup.parse(courseHtml);
        Element table = document.selectFirst(".gridtable");
        Element tbody = table.child(1);
        Elements trs = tbody.children();

        String[][] courseVals = new String[10][7];
        for (int i = 0; i < trs.size(); i++) {
            Element tr = trs.get(i);
            for (int j = 0; j < 7; j++) {
                Element td = tr.getElementById("TD" + (j * 10 + i) + "_0");
                if (td == null) {
                    // 为null表示与上一节课相同
                    courseVals[i][j] = courseVals[i - 1][j];
                } else {
                    courseVals[i][j] = td.text();
                }

            }
        }
        return courseVals;
    }
}
